#UNDERSTAND THE BUSINESS
#It is important for entrepreneurs to know how likely a project is going to be "sucessful" if launched on Kickstarter
#possible factors include not meeting minimum funding goal or the category of the product/service. 
#This project analyzes statistics to find correlation among 13 features of a project 
#to predict a likelihood of a project to be succesful on Kickstarter - that is fully funded 
#Import data set from url: https://www.kaggle.com/kemical/kickstarter-projects/data
#data in 2018
url1 <- "/Users/tamvu/DS4100/Kickstarter Predict/Startup Prediction/kickstarter-projects/ks-projects-201801.csv"
raw_df <- as.data.frame(read.csv(url1), na.strings = c("", "NA", "undefined"))
#original size is 350k+ which is too big data. I will take a sample 30% 
raw_df <- raw_df[sample(nrow(raw_df) * 0.3), ]
library(RMySQL)
Loading required package: DBI
#CONNECT TO MYSQL DATABASE
awsURL <- "mypersonalinstance.cwoxgifwds2r.us-east-1.rds.amazonaws.com"
con <- dbConnect(MySQL(),
                 user = "vut1307",
                 password = "Vuminhtam1307",
                 host = awsURL,
                 port = 3306,
                 dbname = "kickstarterprojects")
#import data into the database
dbWriteTable(con, name = 'dataset', raw_df, overwrite = TRUE)
[1] TRUE
-- RETRIEVE DATA FROM DATABASE
SELECT * FROM dataset;
#UNDERSTAND THE DATA
#There are over 350k entries recorded since April 2009 to December 2017. Total 15 variables 
#Since ID and names are indentity of the data rather than a factor that determines any outcome, 
#one of the variable is the actual outcome of the data: sucessful/failed/canceled/live. 
#the rest are features that can contribute to the state 
#DEFINE OUTCOME "sucessful": 
#states of a project recorded into the dataset
df <- raw_df
table(df$state)

  canceled     failed       live successful  suspended  undefined 
     11605      59080        835      40488        553       1037 
#I am only taking into account projects that are not live or suspended (because there is no result to it yet if so) 
#projects that are not canceled (because the result is not one of the features in the dataset)
#only projects that is determined with final state (either failed/sucessful)
#EXPLORATORY DATA ANALYSIS: analyze to find important factors that indicate the outcome of successful or failed projects 
#percentage of sucessful vs failed
n <- nrow(subset(df, df$state == "successful" | df$state == "failed"))
mytable <- as.data.frame(table(df$state) / n * 100)
slices <- c(59.33, 40.67) 
lbls <- paste(c( "failed", "successful"), slices, "%")
pie(slices, labels = lbls, main="Pie Chart of Sucessful projects")

#RETRIEVE DATA TO EXPLORE 
#What is the trendiest product being proposed on Kickstarter in the sample?
dbGetQuery(con, '
SELECT main_category AS most_popular_category, 
AVG(backers) AS num_backers, 
AVG(usd_goal_real) AS avg_goal,
AVG(usd_pledged_real) AS avg_pledged,
(SELECT country FROM dataset WHERE state = "successful" GROUP BY country ORDER BY count(*) DESC LIMIT 1) AS most_by_country,
COUNT(*) AS number_proposed FROM dataset 
  GROUP BY main_category
  ORDER BY number_proposed DESC LIMIT 1;
')
Decimal MySQL column 1 imported as numeric
#What is the trendiest product being proposed on Kickstarter that is sucessful? 
#What is the trendiest product being proposed on Kickstarter that is failed?
dbGetQuery(con, '
SELECT  * FROM
(SELECT state, main_category AS most_popular_category, 
AVG(backers) AS num_backers, 
AVG(usd_goal_real) AS avg_goal,
AVG(usd_pledged_real) AS avg_pledged, COUNT(*) AS number_proposed 
FROM dataset WHERE state = "successful"
GROUP BY main_category ORDER BY number_proposed DESC LIMIT 1) good
UNION
(SELECT state, main_category AS most_popular_category, 
AVG(backers) AS num_backers, 
AVG(usd_goal_real) AS avg_goal,
AVG(usd_pledged_real) AS avg_pledged, COUNT(*) AS number_proposed 
FROM dataset WHERE state = "failed"
GROUP BY main_category ORDER BY number_proposed DESC LIMIT 1);
')
Decimal MySQL column 2 imported as numeric
#as we can see from queries the most popular projects is Film and Video but they are most likely to fail.
#What is a good ammount of goal for a project to be less likely to fail? MUCH LOWER THAN THE FAILED PROJECTS 
#Compare the successful projects compared to failed projects with same amount of backers but lower goals than average bad goals?
dbGetQuery(con, '
SELECT D1.main_category, D1.goal AS good_goal, D.avg_fail_goal FROM dataset D1
JOIN (SELECT *, AVG(goal) AS avg_fail_goal FROM dataset D2 WHERE D2.state = "failed") D 
ON D1.backers = D.backers AND D1.goal < D.avg_fail_goal
WHERE  D1.state = "successful"
ORDER BY good_goal DESC;
')
-- Get the projects that is active till most recently.
SELECT * FROM dataset WHERE state = "live" AND YEAR(deadline) = 2018 ORDER BY month(deadline) DESC;
#DATA PREPARATION: CLEANING, IMPUTATION
#raw data has 113 entries.
#MISSING VALUES: disregard all entries that have any missing values of any variable 
#which results in 112k entries with total 15 features
df <- na.omit(df)
#CONSTRUCT DATA: calculate the duration of the project
#I want to disregard the effect of actual time because the result will depend on the events have actually happen at the time. 
df$duration <- as.numeric(as.Date(df$deadline) - as.Date(df$launched))
#SHAPE DATA:DUMMY CODE for the catergories
#1 - Art, 2 - Comics, 3 - Crafts, 4 - Dance, 5 - Design, 6 - Fashion, 7 - Film & Video,
#8 - Food, 9 - Games, 10 - Journalism, 11 - Music, 12 - Photography, 13 - Publishing, 14 - Technology
df$encode_category <- as.numeric(df$main_category)
#DUMMY CODE FOR state: successful = 1, failed = 0
df$encode_state <- as.numeric(df$state)/2 - 1
#DUMMY CODE FOR country
df$encode_country <- as.numeric(df$country)
#CLEAN DATA to sucessful/failed only.
#I am only taking into account projects that are not live or suspended (because there is no result to it yet if so) 
#projects that are not canceled (because the result is not one of the features in the dataset)
#only projects that is determined with final state (either failed/sucessful)
# results in 331465 entries
df <- subset(df, df$state == "failed" | df$state == "successful")
#OMIT IRRELEVANT variables: ID and names. results in 13 variables: 12 features and 1 that is the state
#I also omit (sub)category and only consider main_category to be more general
#Omit time variables: deadline and launched
#Money variable like goals and pledged I am choosing the columns in USD only so ommit currency and general goal and pledged columns
nonvars <- c("ID","name", "category", "goal", "launched", "deadline", "pledged", "state", "currency", "main_category", "country", "usd.pledged")
df <- df[,!(names(df) %in% nonvars)]
#EXPLORE DATA: CORRELATION ANALYSIS
cor(df$usd_goal_real, df$encode_state)
[1] -0.02208757
cor(df$backers, df$encode_state) 
[1] 0.1281917
cor(df$duration, df$encode_state)
[1] -0.1199181
cor(df$usd_pledged_real, df$encode_state)
[1] 0.1259346
# the sucessful projects has lower funding goals, lower duration, 
# higher number of backers, and higher pledged in USD
#EXPLORATORY PLOTS - DETECT AND REMOVE OUTLIERS 
#function to detetect and remove outliers
#source https://www.r-bloggers.com/identify-describe-plot-and-remove-the-outliers-from-the-dataset/
outlierKD <- function(dt, var, str) {
  var_name <- eval(substitute(var),eval(dt))
  tot <- sum(!is.na(var_name))
  na1 <- sum(is.na(var_name))
  m1 <- mean(var_name, na.rm = T)
  par(mfrow=c(2, 2), oma=c(0,0,3,0))
  boxplot(var_name, main="With outliers")
  hist(var_name, main="With outliers", xlab=NA, ylab=NA)
  outlier <- boxplot.stats(var_name)$out
  mo <- mean(outlier)
  var_name <- ifelse(var_name %in% outlier, NA, var_name)
  boxplot(var_name, main="Without outliers")
  hist(var_name, main="Without outliers", xlab=NA, ylab=NA)
  title(paste("Outlier Check of", str) , outer=TRUE)
  na2 <- sum(is.na(var_name))
  message("Outliers identified: ", na2 - na1, " from ", tot, " observations")
  message("Proportion (%) of outliers: ", (na2 - na1) / tot*100)
  message("Mean of the outliers: ", mo)
  m2 <- mean(var_name, na.rm = T)
  message("Mean without removing outliers: ", m1)
  message("Mean if we remove outliers: ", m2)
  response <- "yes"
  if(response == "y" | response == "yes"){
    dt[as.character(substitute(var))] <- invisible(var_name)
    assign(as.character(as.list(match.call())$dt), dt, envir = .GlobalEnv)
    message("Outliers successfully removed", "\n")
    return(invisible(dt))
  } else{
    message("Nothing changed", "\n")
    return(invisible(var_name))
  }
}
#remove outliers of number of backers
outlierKD(df,backers, "number of backers")

#remove outliers of pledged number
outlierKD(df,usd_pledged_real, "USD real pledged")

outlierKD(df,usd_goal_real, "goal in USD")

#NORMALIZATION OF DATA
#MIN-MAX NORMALIZATION of backers and duration of fundraising
min_max_norm <- function(data) {
  options(scipen = 999)
  minX <- min(as.numeric(data)) 
  maxX <- max(as.numeric(data))
  data <- (as.numeric(data) - minX)/(maxX - minX)
}
min_max_norm(df$backers)
min_max_norm(df$duration)
#NORMALIZATION OF DATA
#Z-SCORE STANDARDIZATION of goal and pledged
df$usd_goal_real <- scale(df$usd_goal_real)
df$usd_pledged_real <- scale(df$usd_pledged_real)
#DATA MODELING
#Create a stratified sample where you randomly select 70% of successful and failed projects to be part of the validation data set.
df <- na.omit(df)
train_Set <- data.frame()
set.seed(5)
for(i in 0:1) {
  all_of_type_i <- subset(df, df$encode_state == i)
  train_Set <- rbind(train_Set, all_of_type_i[sample(0.7 * nrow(all_of_type_i)),])
}
#The remaining cases will form the training data set. 
test_Set <- df[!(as.numeric(row.names(df)) %in% as.numeric(row.names(train_Set))), ]
#MULTIPLE LINEAR REGRESSION MODEL
lin_model <- lm(train_Set$encode_state ~ ., data = train_Set)
summary(lin_model)

Call:
lm(formula = train_Set$encode_state ~ ., data = train_Set)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.2330 -0.2559 -0.1037  0.2767  0.8127 

Coefficients:
                    Estimate  Std. Error  t value             Pr(>|t|)    
(Intercept)       0.26736775  0.00701379   38.120 < 0.0000000000000002 ***
backers           0.00454958  0.00007458   61.000 < 0.0000000000000002 ***
usd_pledged_real  0.17960752  0.00267593   67.120 < 0.0000000000000002 ***
usd_goal_real    -0.18320886  0.00158543 -115.558 < 0.0000000000000002 ***
duration         -0.00204077  0.00011041  -18.483 < 0.0000000000000002 ***
encode_category   0.00036593  0.00035920    1.019                0.308    
encode_country    0.00126336  0.00023308    5.420         0.0000000598 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3308 on 53081 degrees of freedom
Multiple R-squared:  0.5224,    Adjusted R-squared:  0.5224 
F-statistic:  9678 on 6 and 53081 DF,  p-value: < 0.00000000000000022
#REVISE THE LINEAR REGRESSION
lin_model <- lm(train_Set$encode_state ~ . - encode_category, data = train_Set)
summary(lin_model)

Call:
lm(formula = train_Set$encode_state ~ . - encode_category, data = train_Set)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.2310 -0.2560 -0.1036  0.2767  0.8120 

Coefficients:
                    Estimate  Std. Error  t value             Pr(>|t|)    
(Intercept)       0.27034607  0.00637542   42.404 < 0.0000000000000002 ***
backers           0.00454867  0.00007458   60.992 < 0.0000000000000002 ***
usd_pledged_real  0.17964063  0.00267574   67.137 < 0.0000000000000002 ***
usd_goal_real    -0.18314053  0.00158401 -115.618 < 0.0000000000000002 ***
duration         -0.00203607  0.00011032  -18.457 < 0.0000000000000002 ***
encode_country    0.00126253  0.00023308    5.417          0.000000061 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3308 on 53082 degrees of freedom
Multiple R-squared:  0.5224,    Adjusted R-squared:  0.5224 
F-statistic: 1.161e+04 on 5 and 53082 DF,  p-value: < 0.00000000000000022
#Ideal linear regression model for predicting the state of a project on Kickstarter in this data set 
#include number of backers, USD pledged, USD goal, duration and country. 
#All are significant with P-value < 0.05 
#LOGISTIC REGRESSION MODEL
log_model <- glm(train_Set$encode_state ~ ., data = train_Set)
summary(log_model)

Call:
glm(formula = train_Set$encode_state ~ ., data = train_Set)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.2330  -0.2559  -0.1037   0.2767   0.8127  

Coefficients:
                    Estimate  Std. Error  t value             Pr(>|t|)    
(Intercept)       0.26736775  0.00701379   38.120 < 0.0000000000000002 ***
backers           0.00454958  0.00007458   61.000 < 0.0000000000000002 ***
usd_pledged_real  0.17960752  0.00267593   67.120 < 0.0000000000000002 ***
usd_goal_real    -0.18320886  0.00158543 -115.558 < 0.0000000000000002 ***
duration         -0.00204077  0.00011041  -18.483 < 0.0000000000000002 ***
encode_category   0.00036593  0.00035920    1.019                0.308    
encode_country    0.00126336  0.00023308    5.420         0.0000000598 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for gaussian family taken to be 0.1094063)

    Null deviance: 12160.1  on 53087  degrees of freedom
Residual deviance:  5807.4  on 53081  degrees of freedom
AIC: 33199

Number of Fisher Scoring iterations: 2
#REVISE LOGISTIC REGRESSION MODEL
log_model <- glm(train_Set$encode_state ~ . - encode_category, data = train_Set)
summary(log_model)

Call:
glm(formula = train_Set$encode_state ~ . - encode_category, data = train_Set)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.2310  -0.2560  -0.1036   0.2767   0.8120  

Coefficients:
                    Estimate  Std. Error  t value             Pr(>|t|)    
(Intercept)       0.27034607  0.00637542   42.404 < 0.0000000000000002 ***
backers           0.00454867  0.00007458   60.992 < 0.0000000000000002 ***
usd_pledged_real  0.17964063  0.00267574   67.137 < 0.0000000000000002 ***
usd_goal_real    -0.18314053  0.00158401 -115.618 < 0.0000000000000002 ***
duration         -0.00203607  0.00011032  -18.457 < 0.0000000000000002 ***
encode_country    0.00126253  0.00023308    5.417          0.000000061 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for gaussian family taken to be 0.1094063)

    Null deviance: 12160.1  on 53087  degrees of freedom
Residual deviance:  5807.5  on 53082  degrees of freedom
AIC: 33198

Number of Fisher Scoring iterations: 2
#Ideal multiple regression model for predicting outcome of project in this data set include number of backers, USD pledged, USD goal, duration and country. All are significant with P-value < 0.05 
#From both regression models, category is not a significant idicator of the outcome - which was my hypothesis when I started the project. A protential reason is that the category is only significant regarding the events/trend of the time of the project. For example waste-free trend will elevate a chance of success for a environmental-friendly product like beewax wraper (Bee's Wrap). 
library('class')
#TUNING OF THE K-NN MODEL. 
#Determine an optimal k by trying all values from 5 through 15 for k-NN algorithm against the cases in the validation data set. 
#Source code: https://www.r-bloggers.com/using-knn-classifier-to-predict-whether-the-price-of-stock-will-increase/
min <- 2
max <- 10
accuracy <- rep(0, max-min+1) #initialize with 0 accuracy
k <- min:max
for(x in k){
  prediction <- knn(train_Set, test_Set, train_Set$encode_state, k = x)
  accuracy[x] <- mean(prediction == test_Set$encode_state) * 100 #calculate accuracy
}
#What is the optimal k, i.e., the k that results in the best accuracy? Plot k versus accuracy.
plot(k, accuracy[min:max], type = 'b', main = "Accuracy by k", xlab = "accuracy (%)")

#Most accurate k = 11. REVISE AND BUILD THE K-NN MODEL WITH K = 11
library('class')
knn_model <- knn(train = train_Set, test = test_Set, train_Set$encode_state, k = 3)
table(knn_model, test_Set$encode_state)
         
knn_model     0     1
        0 13823   451
        1   847  7633
# What is the percentage of correct prediction of sucessful projects?
knn_accuracy <- mean(knn_model == test_Set$encode_state) * 100
#helper function to calculate the MAD. linear and logistic regression model has same amount of coefficients
predict_value <- function(data, model, i) {
  return(model$coefficients[[1]]
         + data$backers[i] * model$coefficients[[2]]
         + data$usd.pledged[i] * model$coefficients[[3]]
         + data$usd_pledged_real[i] * model$coefficients[[4]]
         + data$duration[i] * model$coefficients[[5]]
         + data$encode_country[i] * model$coefficients[[6]])
}
MAD <- function(model) {
  sum <- 0
  n <- nrow(df)
  for(i in 1:n) {
    sum <- sum + abs(predict_value(df,model, i) - df$encode_state[i])
  }
  return(sum)
}
#EVALUATE THE FIT OF MODELS
#Linear model: 
sumr_lin <- summary(lin_model)
lin_MAD <- MAD(lin_model)
lin_MSE <- mean(summary(lin_model)$residuals^ 2)
paste("Linear Regression model has R-squared of", sumr_lin$r.squared, "which explains the model fits the training data well; has MAD of", lin_MAD, "which is actually higher than R-squared; has MSE of", lin_MSE)
[1] "Linear Regression model has R-squared of 0.522412900229395 which explains the model fits the training data well; has MAD of  which is actually higher than R-squared; has MSE of 0.109393976331674"
#Logistic model
log_MAD <- MAD(log_model) 
paste("Logistic Regression model has MAD of", log_MAD, "explains the model fits the training data well")
[1] "Logistic Regression model has MAD of  explains the model fits the training data well"
#A function to determine if the model has bias (false positive), e.g predicting a project failed when they actually succeeded
bias <- function(model) {
  prediction <- predict(model, type = "response", newdata = test_Set)
prediction <- ifelse(prediction > 0.5,1,0) #set survival = 1
combine <- data.frame(prediction, test_Set$encode_state)
#filter out all errors the model made
error <- subset(combine, combine$prediction != combine$test_Set.encode_state)
#false positive are the errors with people who are actual survived
falsePositive <- subset(error, error$test_Set.encode_state == 1)
falseNegative <- subset(error, error$test_Set.encode_state == 0)
return(c(nrow(falsePositive)/length(prediction)*100, nrow(falseNegative)/length(prediction)*100))
}
#COMPARE BIAS
falsePos <- bias(log_model)[1] #2%
falseNeg <- bias(log_model)[2] #10%
falsePos_lin <- bias(lin_model)[1] #2%
falseNeg_lin <- bias(lin_model)[2] #10%
paste("Logistic Regression Model and Linear Regression Model has more false positive. Hence they are bias towards predicting a project fail")
[1] "Logistic Regression Model and Linear Regression Model has more false positive. Hence they are bias towards predicting a project fail"
#function get_accuracy: takes in a model and output the percentage of how correct the prediction 
get_accuracy <- function(name, model) {
  prediction <- predict(model, type = "response", newdata = test_Set)
  prediction <- ifelse(prediction > 0.5,1,0) #set survival = 1
#and determine its prediction accuracy (as a percentage correct).
#by comparing the prediction to the test data set survival result
correctPrediction <- mean(prediction == test_Set$encode_state)
return(paste('Prediction Accuracy of', name, 'is', correctPrediction * 100, '%'))
}
#COMPARE THE ACCURACY OF MODELS
paste(get_accuracy("Multiple Linear Regression Model", lin_model), "with R-squared is 0.51")
[1] "Prediction Accuracy of Multiple Linear Regression Model is 86.7495824909906 % with R-squared is 0.51"
get_accuracy("Logistic Regression Model", log_model)
[1] "Prediction Accuracy of Logistic Regression Model is 86.7495824909906 %"
paste("Prediction Accuracy of k-NN Model with k=11 is", knn_accuracy, "%")
[1] "Prediction Accuracy of k-NN Model with k=11 is 94.2955084820251 %"
paste("The regression models have very similar close results. Though linear regression model is has MAD higher than R-squared, both fit the training data well. Compared to k-NN, they are best predictors of a succesful project though has 10% false positive bias.")
[1] "The regression models have very similar close results. Though linear regression model is has MAD higher than R-squared, both fit the training data well. Compared to k-NN, they are best predictors of a succesful project though has 10% false positive bias."
#PREDICT THE LIVE PROJECTS USING MULTIPLE LINEAR REGRESSION: 
#predict the active projects that were live until last month
#format the data frame
active_df <- subset(raw_df, raw_df$ID %in% active_df$ID)
active_df$duration <- as.numeric(as.Date(active_df$deadline) - as.Date(active_df$launched))
active_df$encode_category <- as.numeric(active_df$main_category)
active_df$encode_country <- as.numeric(active_df$country)
#predict successfulness using linear regression
pred_values <- c()
for(i in 1:nrow(active_df)) {
  pred_values <- c(pred_values, predict_value(active_df, lin_model, i))
}
pred_values <- ifelse(pred_values > 0.5,1,0) 
active_df$predict_state <- pred_values
active_df
#get the average data
n <- length(active_df)
good_proj <- subset(active_df, active_df$predict_state == 1)
percentage_succesful <- nrow(good_proj) / n * 100
country <- names(which.max(table(good_proj$country)))
avg_backers <- mean(good_proj$backers)
avg_goal <- mean(good_proj$usd_goal_real)
avg_pledged <- mean(good_proj$usd_pledged_real)
avg_duration <- mean(good_proj$duration)
#INTERPRETATION OF RESULTS: 
paste("On the prediction results of active projects,", percentage_succesful, "% are successful.")
[1] "On the prediction results of active projects, 42.1052631578947 % are successful."
paste("From my average calculation, in order to be successful project on Kickstarter, the creator should aim to launch in ", country, "with a good amount of backers of at least", ceiling(avg_backers), "setting a goal lower than $", avg_goal, "and a duration of fund-raising as short as", floor(avg_duration), "days")
[1] "From my average calculation, in order to be successful project on Kickstarter, the creator should aim to launch in  US with a good amount of backers of at least 4 setting a goal lower than $ 23042.11625 and a duration of fund-raising as short as 46 days"
LS0tCnRpdGxlOiAiS2lja1N0YXJ0IFN0YXJ0dXAgVHJlbmQgUHJlZGljdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpgYGB7cn0KI1VOREVSU1RBTkQgVEhFIEJVU0lORVNTCiNJdCBpcyBpbXBvcnRhbnQgZm9yIGVudHJlcHJlbmV1cnMgdG8ga25vdyBob3cgbGlrZWx5IGEgcHJvamVjdCBpcyBnb2luZyB0byBiZSAic3VjZXNzZnVsIiBpZiBsYXVuY2hlZCBvbiBLaWNrc3RhcnRlcgojcG9zc2libGUgZmFjdG9ycyBpbmNsdWRlIG5vdCBtZWV0aW5nIG1pbmltdW0gZnVuZGluZyBnb2FsIG9yIHRoZSBjYXRlZ29yeSBvZiB0aGUgcHJvZHVjdC9zZXJ2aWNlLiAKI1RoaXMgcHJvamVjdCBhbmFseXplcyBzdGF0aXN0aWNzIHRvIGZpbmQgY29ycmVsYXRpb24gYW1vbmcgMTMgZmVhdHVyZXMgb2YgYSBwcm9qZWN0IAojdG8gcHJlZGljdCBhIGxpa2VsaWhvb2Qgb2YgYSBwcm9qZWN0IHRvIGJlIHN1Y2Nlc2Z1bCBvbiBLaWNrc3RhcnRlciAtIHRoYXQgaXMgZnVsbHkgZnVuZGVkIApgYGAKCgpgYGB7cn0KI0ltcG9ydCBkYXRhIHNldCBmcm9tIHVybDogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9rZW1pY2FsL2tpY2tzdGFydGVyLXByb2plY3RzL2RhdGEKI2RhdGEgaW4gMjAxOAp1cmwxIDwtICIvVXNlcnMvdGFtdnUvRFM0MTAwL0tpY2tzdGFydGVyIFByZWRpY3QvU3RhcnR1cCBQcmVkaWN0aW9uL2tpY2tzdGFydGVyLXByb2plY3RzL2tzLXByb2plY3RzLTIwMTgwMS5jc3YiCnJhd19kZiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHVybDEpLCBuYS5zdHJpbmdzID0gYygiIiwgIk5BIiwgInVuZGVmaW5lZCIpKQojb3JpZ2luYWwgc2l6ZSBpcyAzNTBrKyB3aGljaCBpcyB0b28gYmlnIGRhdGEuIEkgd2lsbCB0YWtlIGEgc2FtcGxlIDMwJSAKcmF3X2RmIDwtIHJhd19kZltzYW1wbGUobnJvdyhyYXdfZGYpICogMC4zKSwgXQpgYGAKCmBgYHtyfQpsaWJyYXJ5KFJNeVNRTCkKI0NPTk5FQ1QgVE8gTVlTUUwgREFUQUJBU0UKYXdzVVJMIDwtICJteXBlcnNvbmFsaW5zdGFuY2UuY3dveGdpZndkczJyLnVzLWVhc3QtMS5yZHMuYW1hem9uYXdzLmNvbSIKY29uIDwtIGRiQ29ubmVjdChNeVNRTCgpLAogICAgICAgICAgICAgICAgIHVzZXIgPSAidnV0MTMwNyIsCiAgICAgICAgICAgICAgICAgcGFzc3dvcmQgPSAiVnVtaW5odGFtMTMwNyIsCiAgICAgICAgICAgICAgICAgaG9zdCA9IGF3c1VSTCwKICAgICAgICAgICAgICAgICBwb3J0ID0gMzMwNiwKICAgICAgICAgICAgICAgICBkYm5hbWUgPSAia2lja3N0YXJ0ZXJwcm9qZWN0cyIpCiNpbXBvcnQgZGF0YSBpbnRvIHRoZSBkYXRhYmFzZQpkYldyaXRlVGFibGUoY29uLCBuYW1lID0gJ2RhdGFzZXQnLCByYXdfZGYsIG92ZXJ3cml0ZSA9IFRSVUUpCmBgYAoKYGBge3NxbCBjb25uZWN0aW9uPSBjb24sIG91dHB1dC52YXIgPSBkZn0KLS0gUkVUUklFVkUgREFUQSBGUk9NIERBVEFCQVNFClNFTEVDVCAqIEZST00gZGF0YXNldDsKYGBgCgpgYGB7cn0KI1VOREVSU1RBTkQgVEhFIERBVEEKI1RoZXJlIGFyZSBvdmVyIDM1MGsgZW50cmllcyByZWNvcmRlZCBzaW5jZSBBcHJpbCAyMDA5IHRvIERlY2VtYmVyIDIwMTcuIFRvdGFsIDE1IHZhcmlhYmxlcyAKI1NpbmNlIElEIGFuZCBuYW1lcyBhcmUgaW5kZW50aXR5IG9mIHRoZSBkYXRhIHJhdGhlciB0aGFuIGEgZmFjdG9yIHRoYXQgZGV0ZXJtaW5lcyBhbnkgb3V0Y29tZSwgCiNvbmUgb2YgdGhlIHZhcmlhYmxlIGlzIHRoZSBhY3R1YWwgb3V0Y29tZSBvZiB0aGUgZGF0YTogc3VjZXNzZnVsL2ZhaWxlZC9jYW5jZWxlZC9saXZlLiAKI3RoZSByZXN0IGFyZSBmZWF0dXJlcyB0aGF0IGNhbiBjb250cmlidXRlIHRvIHRoZSBzdGF0ZSAKI0RFRklORSBPVVRDT01FICJzdWNlc3NmdWwiOiAKI3N0YXRlcyBvZiBhIHByb2plY3QgcmVjb3JkZWQgaW50byB0aGUgZGF0YXNldApkZiA8LSByYXdfZGYKdGFibGUoZGYkc3RhdGUpCiNJIGFtIG9ubHkgdGFraW5nIGludG8gYWNjb3VudCBwcm9qZWN0cyB0aGF0IGFyZSBub3QgbGl2ZSBvciBzdXNwZW5kZWQgKGJlY2F1c2UgdGhlcmUgaXMgbm8gcmVzdWx0IHRvIGl0IHlldCBpZiBzbykgCiNwcm9qZWN0cyB0aGF0IGFyZSBub3QgY2FuY2VsZWQgKGJlY2F1c2UgdGhlIHJlc3VsdCBpcyBub3Qgb25lIG9mIHRoZSBmZWF0dXJlcyBpbiB0aGUgZGF0YXNldCkKI29ubHkgcHJvamVjdHMgdGhhdCBpcyBkZXRlcm1pbmVkIHdpdGggZmluYWwgc3RhdGUgKGVpdGhlciBmYWlsZWQvc3VjZXNzZnVsKQpgYGAKCmBgYHtyfQojRVhQTE9SQVRPUlkgREFUQSBBTkFMWVNJUzogYW5hbHl6ZSB0byBmaW5kIGltcG9ydGFudCBmYWN0b3JzIHRoYXQgaW5kaWNhdGUgdGhlIG91dGNvbWUgb2Ygc3VjY2Vzc2Z1bCBvciBmYWlsZWQgcHJvamVjdHMgCiNwZXJjZW50YWdlIG9mIHN1Y2Vzc2Z1bCB2cyBmYWlsZWQKbiA8LSBucm93KHN1YnNldChkZiwgZGYkc3RhdGUgPT0gInN1Y2Nlc3NmdWwiIHwgZGYkc3RhdGUgPT0gImZhaWxlZCIpKQpteXRhYmxlIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoZGYkc3RhdGUpIC8gbiAqIDEwMCkKc2xpY2VzIDwtIGMoNTkuMzMsIDQwLjY3KSAKbGJscyA8LSBwYXN0ZShjKCAiZmFpbGVkIiwgInN1Y2Nlc3NmdWwiKSwgc2xpY2VzLCAiJSIpCnBpZShzbGljZXMsIGxhYmVscyA9IGxibHMsIG1haW49IlBpZSBDaGFydCBvZiBTdWNlc3NmdWwgcHJvamVjdHMiKQpgYGAKCmBgYHtyfQojUkVUUklFVkUgREFUQSBUTyBFWFBMT1JFIAojV2hhdCBpcyB0aGUgdHJlbmRpZXN0IHByb2R1Y3QgYmVpbmcgcHJvcG9zZWQgb24gS2lja3N0YXJ0ZXIgaW4gdGhlIHNhbXBsZT8KZGJHZXRRdWVyeShjb24sICcKU0VMRUNUIG1haW5fY2F0ZWdvcnkgQVMgbW9zdF9wb3B1bGFyX2NhdGVnb3J5LCAKQVZHKGJhY2tlcnMpIEFTIG51bV9iYWNrZXJzLCAKQVZHKHVzZF9nb2FsX3JlYWwpIEFTIGF2Z19nb2FsLApBVkcodXNkX3BsZWRnZWRfcmVhbCkgQVMgYXZnX3BsZWRnZWQsCihTRUxFQ1QgY291bnRyeSBGUk9NIGRhdGFzZXQgV0hFUkUgc3RhdGUgPSAic3VjY2Vzc2Z1bCIgR1JPVVAgQlkgY291bnRyeSBPUkRFUiBCWSBjb3VudCgqKSBERVNDIExJTUlUIDEpIEFTIG1vc3RfYnlfY291bnRyeSwKQ09VTlQoKikgQVMgbnVtYmVyX3Byb3Bvc2VkIEZST00gZGF0YXNldCAKICBHUk9VUCBCWSBtYWluX2NhdGVnb3J5CiAgT1JERVIgQlkgbnVtYmVyX3Byb3Bvc2VkIERFU0MgTElNSVQgMTsKJykKYGBgCgpgYGB7cn0KI1doYXQgaXMgdGhlIHRyZW5kaWVzdCBwcm9kdWN0IGJlaW5nIHByb3Bvc2VkIG9uIEtpY2tzdGFydGVyIHRoYXQgaXMgc3VjZXNzZnVsPyAKI1doYXQgaXMgdGhlIHRyZW5kaWVzdCBwcm9kdWN0IGJlaW5nIHByb3Bvc2VkIG9uIEtpY2tzdGFydGVyIHRoYXQgaXMgZmFpbGVkPwpkYkdldFF1ZXJ5KGNvbiwgJwpTRUxFQ1QgICogRlJPTQooU0VMRUNUIHN0YXRlLCBtYWluX2NhdGVnb3J5IEFTIG1vc3RfcG9wdWxhcl9jYXRlZ29yeSwgCkFWRyhiYWNrZXJzKSBBUyBudW1fYmFja2VycywgCkFWRyh1c2RfZ29hbF9yZWFsKSBBUyBhdmdfZ29hbCwKQVZHKHVzZF9wbGVkZ2VkX3JlYWwpIEFTIGF2Z19wbGVkZ2VkLCBDT1VOVCgqKSBBUyBudW1iZXJfcHJvcG9zZWQgCkZST00gZGF0YXNldCBXSEVSRSBzdGF0ZSA9ICJzdWNjZXNzZnVsIgpHUk9VUCBCWSBtYWluX2NhdGVnb3J5IE9SREVSIEJZIG51bWJlcl9wcm9wb3NlZCBERVNDIExJTUlUIDEpIGdvb2QKVU5JT04KKFNFTEVDVCBzdGF0ZSwgbWFpbl9jYXRlZ29yeSBBUyBtb3N0X3BvcHVsYXJfY2F0ZWdvcnksIApBVkcoYmFja2VycykgQVMgbnVtX2JhY2tlcnMsIApBVkcodXNkX2dvYWxfcmVhbCkgQVMgYXZnX2dvYWwsCkFWRyh1c2RfcGxlZGdlZF9yZWFsKSBBUyBhdmdfcGxlZGdlZCwgQ09VTlQoKikgQVMgbnVtYmVyX3Byb3Bvc2VkIApGUk9NIGRhdGFzZXQgV0hFUkUgc3RhdGUgPSAiZmFpbGVkIgpHUk9VUCBCWSBtYWluX2NhdGVnb3J5IE9SREVSIEJZIG51bWJlcl9wcm9wb3NlZCBERVNDIExJTUlUIDEpOwonKQoKI2FzIHdlIGNhbiBzZWUgZnJvbSBxdWVyaWVzIHRoZSBtb3N0IHBvcHVsYXIgcHJvamVjdHMgaXMgRmlsbSBhbmQgVmlkZW8gYnV0IHRoZXkgYXJlIG1vc3QgbGlrZWx5IHRvIGZhaWwuCmBgYAoKYGBge3J9CiNXaGF0IGlzIGEgZ29vZCBhbW1vdW50IG9mIGdvYWwgZm9yIGEgcHJvamVjdCB0byBiZSBsZXNzIGxpa2VseSB0byBmYWlsPyBNVUNIIExPV0VSIFRIQU4gVEhFIEZBSUxFRCBQUk9KRUNUUyAKI0NvbXBhcmUgdGhlIHN1Y2Nlc3NmdWwgcHJvamVjdHMgY29tcGFyZWQgdG8gZmFpbGVkIHByb2plY3RzIHdpdGggc2FtZSBhbW91bnQgb2YgYmFja2VycyBidXQgbG93ZXIgZ29hbHMgdGhhbiBhdmVyYWdlIGJhZCBnb2Fscz8KZGJHZXRRdWVyeShjb24sICcKU0VMRUNUIEQxLm1haW5fY2F0ZWdvcnksIEQxLmdvYWwgQVMgZ29vZF9nb2FsLCBELmF2Z19mYWlsX2dvYWwgRlJPTSBkYXRhc2V0IEQxCkpPSU4gKFNFTEVDVCAqLCBBVkcoZ29hbCkgQVMgYXZnX2ZhaWxfZ29hbCBGUk9NIGRhdGFzZXQgRDIgV0hFUkUgRDIuc3RhdGUgPSAiZmFpbGVkIikgRCAKT04gRDEuYmFja2VycyA9IEQuYmFja2VycyBBTkQgRDEuZ29hbCA8IEQuYXZnX2ZhaWxfZ29hbApXSEVSRSAgRDEuc3RhdGUgPSAic3VjY2Vzc2Z1bCIKT1JERVIgQlkgZ29vZF9nb2FsIERFU0M7CicpCmBgYAoKYGBge3NxbCBjb25uZWN0aW9uPSBjb24sIG91dHB1dC52YXIgPSBhY3RpdmVfZGZ9Ci0tIEdldCB0aGUgcHJvamVjdHMgdGhhdCBpcyBhY3RpdmUgdGlsbCBtb3N0IHJlY2VudGx5LgpTRUxFQ1QgKiBGUk9NIGRhdGFzZXQgV0hFUkUgc3RhdGUgPSAibGl2ZSIgQU5EIFlFQVIoZGVhZGxpbmUpID0gMjAxOCBPUkRFUiBCWSBtb250aChkZWFkbGluZSkgREVTQzsKYGBgCgoKYGBge3J9CiNEQVRBIFBSRVBBUkFUSU9OOiBDTEVBTklORywgSU1QVVRBVElPTgojcmF3IGRhdGEgaGFzIDExMyBlbnRyaWVzLgojTUlTU0lORyBWQUxVRVM6IGRpc3JlZ2FyZCBhbGwgZW50cmllcyB0aGF0IGhhdmUgYW55IG1pc3NpbmcgdmFsdWVzIG9mIGFueSB2YXJpYWJsZSAKI3doaWNoIHJlc3VsdHMgaW4gMTEyayBlbnRyaWVzIHdpdGggdG90YWwgMTUgZmVhdHVyZXMKZGYgPC0gbmEub21pdChkZikKYGBgCgpgYGB7cn0KI0NPTlNUUlVDVCBEQVRBOiBjYWxjdWxhdGUgdGhlIGR1cmF0aW9uIG9mIHRoZSBwcm9qZWN0CiNJIHdhbnQgdG8gZGlzcmVnYXJkIHRoZSBlZmZlY3Qgb2YgYWN0dWFsIHRpbWUgYmVjYXVzZSB0aGUgcmVzdWx0IHdpbGwgZGVwZW5kIG9uIHRoZSBldmVudHMgaGF2ZSBhY3R1YWxseSBoYXBwZW4gYXQgdGhlIHRpbWUuIApkZiRkdXJhdGlvbiA8LSBhcy5udW1lcmljKGFzLkRhdGUoZGYkZGVhZGxpbmUpIC0gYXMuRGF0ZShkZiRsYXVuY2hlZCkpCiNTSEFQRSBEQVRBOkRVTU1ZIENPREUgZm9yIHRoZSBjYXRlcmdvcmllcwojMSAtIEFydCwgMiAtIENvbWljcywgMyAtIENyYWZ0cywgNCAtIERhbmNlLCA1IC0gRGVzaWduLCA2IC0gRmFzaGlvbiwgNyAtIEZpbG0gJiBWaWRlbywKIzggLSBGb29kLCA5IC0gR2FtZXMsIDEwIC0gSm91cm5hbGlzbSwgMTEgLSBNdXNpYywgMTIgLSBQaG90b2dyYXBoeSwgMTMgLSBQdWJsaXNoaW5nLCAxNCAtIFRlY2hub2xvZ3kKZGYkZW5jb2RlX2NhdGVnb3J5IDwtIGFzLm51bWVyaWMoZGYkbWFpbl9jYXRlZ29yeSkKI0RVTU1ZIENPREUgRk9SIHN0YXRlOiBzdWNjZXNzZnVsID0gMSwgZmFpbGVkID0gMApkZiRlbmNvZGVfc3RhdGUgPC0gYXMubnVtZXJpYyhkZiRzdGF0ZSkvMiAtIDEKI0RVTU1ZIENPREUgRk9SIGNvdW50cnkKZGYkZW5jb2RlX2NvdW50cnkgPC0gYXMubnVtZXJpYyhkZiRjb3VudHJ5KQpgYGAKCmBgYHtyfQojQ0xFQU4gREFUQSB0byBzdWNlc3NmdWwvZmFpbGVkIG9ubHkuCiNJIGFtIG9ubHkgdGFraW5nIGludG8gYWNjb3VudCBwcm9qZWN0cyB0aGF0IGFyZSBub3QgbGl2ZSBvciBzdXNwZW5kZWQgKGJlY2F1c2UgdGhlcmUgaXMgbm8gcmVzdWx0IHRvIGl0IHlldCBpZiBzbykgCiNwcm9qZWN0cyB0aGF0IGFyZSBub3QgY2FuY2VsZWQgKGJlY2F1c2UgdGhlIHJlc3VsdCBpcyBub3Qgb25lIG9mIHRoZSBmZWF0dXJlcyBpbiB0aGUgZGF0YXNldCkKI29ubHkgcHJvamVjdHMgdGhhdCBpcyBkZXRlcm1pbmVkIHdpdGggZmluYWwgc3RhdGUgKGVpdGhlciBmYWlsZWQvc3VjZXNzZnVsKQojIHJlc3VsdHMgaW4gMzMxNDY1IGVudHJpZXMKZGYgPC0gc3Vic2V0KGRmLCBkZiRzdGF0ZSA9PSAiZmFpbGVkIiB8IGRmJHN0YXRlID09ICJzdWNjZXNzZnVsIikKI09NSVQgSVJSRUxFVkFOVCB2YXJpYWJsZXM6IElEIGFuZCBuYW1lcy4gcmVzdWx0cyBpbiAxMyB2YXJpYWJsZXM6IDEyIGZlYXR1cmVzIGFuZCAxIHRoYXQgaXMgdGhlIHN0YXRlCiNJIGFsc28gb21pdCAoc3ViKWNhdGVnb3J5IGFuZCBvbmx5IGNvbnNpZGVyIG1haW5fY2F0ZWdvcnkgdG8gYmUgbW9yZSBnZW5lcmFsCiNPbWl0IHRpbWUgdmFyaWFibGVzOiBkZWFkbGluZSBhbmQgbGF1bmNoZWQKI01vbmV5IHZhcmlhYmxlIGxpa2UgZ29hbHMgYW5kIHBsZWRnZWQgSSBhbSBjaG9vc2luZyB0aGUgY29sdW1ucyBpbiBVU0Qgb25seSBzbyBvbW1pdCBjdXJyZW5jeSBhbmQgZ2VuZXJhbCBnb2FsIGFuZCBwbGVkZ2VkIGNvbHVtbnMKbm9udmFycyA8LSBjKCJJRCIsIm5hbWUiLCAiY2F0ZWdvcnkiLCAiZ29hbCIsICJsYXVuY2hlZCIsICJkZWFkbGluZSIsICJwbGVkZ2VkIiwgInN0YXRlIiwgImN1cnJlbmN5IiwgIm1haW5fY2F0ZWdvcnkiLCAiY291bnRyeSIsICJ1c2QucGxlZGdlZCIpCmRmIDwtIGRmWywhKG5hbWVzKGRmKSAlaW4lIG5vbnZhcnMpXQpgYGAKCgpgYGB7cn0KI0VYUExPUkUgREFUQTogQ09SUkVMQVRJT04gQU5BTFlTSVMKY29yKGRmJHVzZF9nb2FsX3JlYWwsIGRmJGVuY29kZV9zdGF0ZSkKY29yKGRmJGJhY2tlcnMsIGRmJGVuY29kZV9zdGF0ZSkgCmNvcihkZiRkdXJhdGlvbiwgZGYkZW5jb2RlX3N0YXRlKQpjb3IoZGYkdXNkX3BsZWRnZWRfcmVhbCwgZGYkZW5jb2RlX3N0YXRlKQojIHRoZSBzdWNlc3NmdWwgcHJvamVjdHMgaGFzIGxvd2VyIGZ1bmRpbmcgZ29hbHMsIGxvd2VyIGR1cmF0aW9uLCAKIyBoaWdoZXIgbnVtYmVyIG9mIGJhY2tlcnMsIGFuZCBoaWdoZXIgcGxlZGdlZCBpbiBVU0QKYGBgCgoKYGBge3J9CiNFWFBMT1JBVE9SWSBQTE9UUyAtIERFVEVDVCBBTkQgUkVNT1ZFIE9VVExJRVJTIAojZnVuY3Rpb24gdG8gZGV0ZXRlY3QgYW5kIHJlbW92ZSBvdXRsaWVycwojc291cmNlIGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL2lkZW50aWZ5LWRlc2NyaWJlLXBsb3QtYW5kLXJlbW92ZS10aGUtb3V0bGllcnMtZnJvbS10aGUtZGF0YXNldC8Kb3V0bGllcktEIDwtIGZ1bmN0aW9uKGR0LCB2YXIsIHN0cikgewogIHZhcl9uYW1lIDwtIGV2YWwoc3Vic3RpdHV0ZSh2YXIpLGV2YWwoZHQpKQogIHRvdCA8LSBzdW0oIWlzLm5hKHZhcl9uYW1lKSkKICBuYTEgPC0gc3VtKGlzLm5hKHZhcl9uYW1lKSkKICBtMSA8LSBtZWFuKHZhcl9uYW1lLCBuYS5ybSA9IFQpCiAgcGFyKG1mcm93PWMoMiwgMiksIG9tYT1jKDAsMCwzLDApKQogIGJveHBsb3QodmFyX25hbWUsIG1haW49IldpdGggb3V0bGllcnMiKQogIGhpc3QodmFyX25hbWUsIG1haW49IldpdGggb3V0bGllcnMiLCB4bGFiPU5BLCB5bGFiPU5BKQogIG91dGxpZXIgPC0gYm94cGxvdC5zdGF0cyh2YXJfbmFtZSkkb3V0CiAgbW8gPC0gbWVhbihvdXRsaWVyKQogIHZhcl9uYW1lIDwtIGlmZWxzZSh2YXJfbmFtZSAlaW4lIG91dGxpZXIsIE5BLCB2YXJfbmFtZSkKICBib3hwbG90KHZhcl9uYW1lLCBtYWluPSJXaXRob3V0IG91dGxpZXJzIikKICBoaXN0KHZhcl9uYW1lLCBtYWluPSJXaXRob3V0IG91dGxpZXJzIiwgeGxhYj1OQSwgeWxhYj1OQSkKICB0aXRsZShwYXN0ZSgiT3V0bGllciBDaGVjayBvZiIsIHN0cikgLCBvdXRlcj1UUlVFKQogIG5hMiA8LSBzdW0oaXMubmEodmFyX25hbWUpKQogIG1lc3NhZ2UoIk91dGxpZXJzIGlkZW50aWZpZWQ6ICIsIG5hMiAtIG5hMSwgIiBmcm9tICIsIHRvdCwgIiBvYnNlcnZhdGlvbnMiKQogIG1lc3NhZ2UoIlByb3BvcnRpb24gKCUpIG9mIG91dGxpZXJzOiAiLCAobmEyIC0gbmExKSAvIHRvdCoxMDApCiAgbWVzc2FnZSgiTWVhbiBvZiB0aGUgb3V0bGllcnM6ICIsIG1vKQogIG0yIDwtIG1lYW4odmFyX25hbWUsIG5hLnJtID0gVCkKICBtZXNzYWdlKCJNZWFuIHdpdGhvdXQgcmVtb3Zpbmcgb3V0bGllcnM6ICIsIG0xKQogIG1lc3NhZ2UoIk1lYW4gaWYgd2UgcmVtb3ZlIG91dGxpZXJzOiAiLCBtMikKICByZXNwb25zZSA8LSAieWVzIgogIGlmKHJlc3BvbnNlID09ICJ5IiB8IHJlc3BvbnNlID09ICJ5ZXMiKXsKICAgIGR0W2FzLmNoYXJhY3RlcihzdWJzdGl0dXRlKHZhcikpXSA8LSBpbnZpc2libGUodmFyX25hbWUpCiAgICBhc3NpZ24oYXMuY2hhcmFjdGVyKGFzLmxpc3QobWF0Y2guY2FsbCgpKSRkdCksIGR0LCBlbnZpciA9IC5HbG9iYWxFbnYpCiAgICBtZXNzYWdlKCJPdXRsaWVycyBzdWNjZXNzZnVsbHkgcmVtb3ZlZCIsICJcbiIpCiAgICByZXR1cm4oaW52aXNpYmxlKGR0KSkKICB9IGVsc2V7CiAgICBtZXNzYWdlKCJOb3RoaW5nIGNoYW5nZWQiLCAiXG4iKQogICAgcmV0dXJuKGludmlzaWJsZSh2YXJfbmFtZSkpCiAgfQp9CgpgYGAKCgpgYGB7cn0KI3JlbW92ZSBvdXRsaWVycyBvZiBudW1iZXIgb2YgYmFja2VycwpvdXRsaWVyS0QoZGYsYmFja2VycywgIm51bWJlciBvZiBiYWNrZXJzIikKI3JlbW92ZSBvdXRsaWVycyBvZiBwbGVkZ2VkIG51bWJlcgpvdXRsaWVyS0QoZGYsdXNkX3BsZWRnZWRfcmVhbCwgIlVTRCByZWFsIHBsZWRnZWQiKQpvdXRsaWVyS0QoZGYsdXNkX2dvYWxfcmVhbCwgImdvYWwgaW4gVVNEIikKYGBgCgpgYGB7cn0KI05PUk1BTElaQVRJT04gT0YgREFUQQojTUlOLU1BWCBOT1JNQUxJWkFUSU9OIG9mIGJhY2tlcnMgYW5kIGR1cmF0aW9uIG9mIGZ1bmRyYWlzaW5nCm1pbl9tYXhfbm9ybSA8LSBmdW5jdGlvbihkYXRhKSB7CiAgb3B0aW9ucyhzY2lwZW4gPSA5OTkpCiAgbWluWCA8LSBtaW4oYXMubnVtZXJpYyhkYXRhKSkgCiAgbWF4WCA8LSBtYXgoYXMubnVtZXJpYyhkYXRhKSkKICBkYXRhIDwtIChhcy5udW1lcmljKGRhdGEpIC0gbWluWCkvKG1heFggLSBtaW5YKQp9Cm1pbl9tYXhfbm9ybShkZiRiYWNrZXJzKQptaW5fbWF4X25vcm0oZGYkZHVyYXRpb24pCgpgYGAKCmBgYHtyfQojTk9STUFMSVpBVElPTiBPRiBEQVRBCiNaLVNDT1JFIFNUQU5EQVJESVpBVElPTiBvZiBnb2FsIGFuZCBwbGVkZ2VkCmRmJHVzZF9nb2FsX3JlYWwgPC0gc2NhbGUoZGYkdXNkX2dvYWxfcmVhbCkKZGYkdXNkX3BsZWRnZWRfcmVhbCA8LSBzY2FsZShkZiR1c2RfcGxlZGdlZF9yZWFsKQpgYGAKCgpgYGB7cn0KI0RBVEEgTU9ERUxJTkcKI0NyZWF0ZSBhIHN0cmF0aWZpZWQgc2FtcGxlIHdoZXJlIHlvdSByYW5kb21seSBzZWxlY3QgNzAlIG9mIHN1Y2Nlc3NmdWwgYW5kIGZhaWxlZCBwcm9qZWN0cyB0byBiZSBwYXJ0IG9mIHRoZSB2YWxpZGF0aW9uIGRhdGEgc2V0LgpkZiA8LSBuYS5vbWl0KGRmKQp0cmFpbl9TZXQgPC0gZGF0YS5mcmFtZSgpCnNldC5zZWVkKDUpCmZvcihpIGluIDA6MSkgewogIGFsbF9vZl90eXBlX2kgPC0gc3Vic2V0KGRmLCBkZiRlbmNvZGVfc3RhdGUgPT0gaSkKICB0cmFpbl9TZXQgPC0gcmJpbmQodHJhaW5fU2V0LCBhbGxfb2ZfdHlwZV9pW3NhbXBsZSgwLjcgKiBucm93KGFsbF9vZl90eXBlX2kpKSxdKQp9CgojVGhlIHJlbWFpbmluZyBjYXNlcyB3aWxsIGZvcm0gdGhlIHRyYWluaW5nIGRhdGEgc2V0LiAKdGVzdF9TZXQgPC0gZGZbIShhcy5udW1lcmljKHJvdy5uYW1lcyhkZikpICVpbiUgYXMubnVtZXJpYyhyb3cubmFtZXModHJhaW5fU2V0KSkpLCBdCmBgYAoKYGBge3J9CiNNVUxUSVBMRSBMSU5FQVIgUkVHUkVTU0lPTiBNT0RFTApsaW5fbW9kZWwgPC0gbG0odHJhaW5fU2V0JGVuY29kZV9zdGF0ZSB+IC4sIGRhdGEgPSB0cmFpbl9TZXQpCnN1bW1hcnkobGluX21vZGVsKQpgYGAKCmBgYHtyfQojUkVWSVNFIFRIRSBMSU5FQVIgUkVHUkVTU0lPTgpsaW5fbW9kZWwgPC0gbG0odHJhaW5fU2V0JGVuY29kZV9zdGF0ZSB+IC4gLSBlbmNvZGVfY2F0ZWdvcnksIGRhdGEgPSB0cmFpbl9TZXQpCnN1bW1hcnkobGluX21vZGVsKQojSWRlYWwgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIHByZWRpY3RpbmcgdGhlIHN0YXRlIG9mIGEgcHJvamVjdCBvbiBLaWNrc3RhcnRlciBpbiB0aGlzIGRhdGEgc2V0IAojaW5jbHVkZSBudW1iZXIgb2YgYmFja2VycywgVVNEIHBsZWRnZWQsIFVTRCBnb2FsLCBkdXJhdGlvbiBhbmQgY291bnRyeS4gCiNBbGwgYXJlIHNpZ25pZmljYW50IHdpdGggUC12YWx1ZSA8IDAuMDUgCmBgYAoKYGBge3J9CiNMT0dJU1RJQyBSRUdSRVNTSU9OIE1PREVMCmxvZ19tb2RlbCA8LSBnbG0odHJhaW5fU2V0JGVuY29kZV9zdGF0ZSB+IC4sIGRhdGEgPSB0cmFpbl9TZXQpCnN1bW1hcnkobG9nX21vZGVsKQpgYGAKCmBgYHtyfQojUkVWSVNFIExPR0lTVElDIFJFR1JFU1NJT04gTU9ERUwKbG9nX21vZGVsIDwtIGdsbSh0cmFpbl9TZXQkZW5jb2RlX3N0YXRlIH4gLiAtIGVuY29kZV9jYXRlZ29yeSwgZGF0YSA9IHRyYWluX1NldCkKc3VtbWFyeShsb2dfbW9kZWwpCiNJZGVhbCBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsIGZvciBwcmVkaWN0aW5nIG91dGNvbWUgb2YgcHJvamVjdCBpbiB0aGlzIGRhdGEgc2V0IGluY2x1ZGUgbnVtYmVyIG9mIGJhY2tlcnMsIFVTRCBwbGVkZ2VkLCBVU0QgZ29hbCwgZHVyYXRpb24gYW5kIGNvdW50cnkuIEFsbCBhcmUgc2lnbmlmaWNhbnQgd2l0aCBQLXZhbHVlIDwgMC4wNSAKYGBgCgpgYGB7cn0KI0Zyb20gYm90aCByZWdyZXNzaW9uIG1vZGVscywgY2F0ZWdvcnkgaXMgbm90IGEgc2lnbmlmaWNhbnQgaWRpY2F0b3Igb2YgdGhlIG91dGNvbWUgLSB3aGljaCB3YXMgbXkgaHlwb3RoZXNpcyB3aGVuIEkgc3RhcnRlZCB0aGUgcHJvamVjdC4gQSBwcm90ZW50aWFsIHJlYXNvbiBpcyB0aGF0IHRoZSBjYXRlZ29yeSBpcyBvbmx5IHNpZ25pZmljYW50IHJlZ2FyZGluZyB0aGUgZXZlbnRzL3RyZW5kIG9mIHRoZSB0aW1lIG9mIHRoZSBwcm9qZWN0LiBGb3IgZXhhbXBsZSB3YXN0ZS1mcmVlIHRyZW5kIHdpbGwgZWxldmF0ZSBhIGNoYW5jZSBvZiBzdWNjZXNzIGZvciBhIGVudmlyb25tZW50YWwtZnJpZW5kbHkgcHJvZHVjdCBsaWtlIGJlZXdheCB3cmFwZXIgKEJlZSdzIFdyYXApLiAKYGBgCgoKCgpgYGB7cn0KbGlicmFyeSgnY2xhc3MnKQojVFVOSU5HIE9GIFRIRSBLLU5OIE1PREVMLiAKI0RldGVybWluZSBhbiBvcHRpbWFsIGsgYnkgdHJ5aW5nIGFsbCB2YWx1ZXMgZnJvbSA1IHRocm91Z2ggMTUgZm9yIGstTk4gYWxnb3JpdGhtIGFnYWluc3QgdGhlIGNhc2VzIGluIHRoZSB2YWxpZGF0aW9uIGRhdGEgc2V0LiAKI1NvdXJjZSBjb2RlOiBodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS91c2luZy1rbm4tY2xhc3NpZmllci10by1wcmVkaWN0LXdoZXRoZXItdGhlLXByaWNlLW9mLXN0b2NrLXdpbGwtaW5jcmVhc2UvCm1pbiA8LSAyCm1heCA8LSAxMAphY2N1cmFjeSA8LSByZXAoMCwgbWF4LW1pbisxKSAjaW5pdGlhbGl6ZSB3aXRoIDAgYWNjdXJhY3kKayA8LSBtaW46bWF4CmZvcih4IGluIGspewogIHByZWRpY3Rpb24gPC0ga25uKHRyYWluX1NldCwgdGVzdF9TZXQsIHRyYWluX1NldCRlbmNvZGVfc3RhdGUsIGsgPSB4KQogIGFjY3VyYWN5W3hdIDwtIG1lYW4ocHJlZGljdGlvbiA9PSB0ZXN0X1NldCRlbmNvZGVfc3RhdGUpICogMTAwICNjYWxjdWxhdGUgYWNjdXJhY3kKfQoKI1doYXQgaXMgdGhlIG9wdGltYWwgaywgaS5lLiwgdGhlIGsgdGhhdCByZXN1bHRzIGluIHRoZSBiZXN0IGFjY3VyYWN5PyBQbG90IGsgdmVyc3VzIGFjY3VyYWN5LgpwbG90KGssIGFjY3VyYWN5W21pbjptYXhdLCB0eXBlID0gJ2InLCBtYWluID0gIkFjY3VyYWN5IGJ5IGsiLCB4bGFiID0gImFjY3VyYWN5ICglKSIpCmBgYAoKYGBge3J9CiNNb3N0IGFjY3VyYXRlIGsgPSAxMS4gUkVWSVNFIEFORCBCVUlMRCBUSEUgSy1OTiBNT0RFTCBXSVRIIEsgPSAxMQpsaWJyYXJ5KCdjbGFzcycpCmtubl9tb2RlbCA8LSBrbm4odHJhaW4gPSB0cmFpbl9TZXQsIHRlc3QgPSB0ZXN0X1NldCwgdHJhaW5fU2V0JGVuY29kZV9zdGF0ZSwgayA9IDMpCnRhYmxlKGtubl9tb2RlbCwgdGVzdF9TZXQkZW5jb2RlX3N0YXRlKQojIFdoYXQgaXMgdGhlIHBlcmNlbnRhZ2Ugb2YgY29ycmVjdCBwcmVkaWN0aW9uIG9mIHN1Y2Vzc2Z1bCBwcm9qZWN0cz8Ka25uX2FjY3VyYWN5IDwtIG1lYW4oa25uX21vZGVsID09IHRlc3RfU2V0JGVuY29kZV9zdGF0ZSkgKiAxMDAKYGBgCgoKYGBge3J9CiNoZWxwZXIgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBNQUQuIGxpbmVhciBhbmQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBoYXMgc2FtZSBhbW91bnQgb2YgY29lZmZpY2llbnRzCnByZWRpY3RfdmFsdWUgPC0gZnVuY3Rpb24oZGF0YSwgbW9kZWwsIGkpIHsKICByZXR1cm4obW9kZWwkY29lZmZpY2llbnRzW1sxXV0KICAgICAgICAgKyBkYXRhJGJhY2tlcnNbaV0gKiBtb2RlbCRjb2VmZmljaWVudHNbWzJdXQogICAgICAgICArIGRhdGEkdXNkLnBsZWRnZWRbaV0gKiBtb2RlbCRjb2VmZmljaWVudHNbWzNdXQogICAgICAgICArIGRhdGEkdXNkX3BsZWRnZWRfcmVhbFtpXSAqIG1vZGVsJGNvZWZmaWNpZW50c1tbNF1dCiAgICAgICAgICsgZGF0YSRkdXJhdGlvbltpXSAqIG1vZGVsJGNvZWZmaWNpZW50c1tbNV1dCiAgICAgICAgICsgZGF0YSRlbmNvZGVfY291bnRyeVtpXSAqIG1vZGVsJGNvZWZmaWNpZW50c1tbNl1dKQp9CgpNQUQgPC0gZnVuY3Rpb24obW9kZWwpIHsKICBzdW0gPC0gMAogIG4gPC0gbnJvdyhkZikKICBmb3IoaSBpbiAxOm4pIHsKICAgIHN1bSA8LSBzdW0gKyBhYnMocHJlZGljdF92YWx1ZShkZixtb2RlbCwgaSkgLSBkZiRlbmNvZGVfc3RhdGVbaV0pCiAgfQogIHJldHVybihzdW0pCn0KYGBgCgpgYGB7cn0KI0VWQUxVQVRFIFRIRSBGSVQgT0YgTU9ERUxTCiNMaW5lYXIgbW9kZWw6IApzdW1yX2xpbiA8LSBzdW1tYXJ5KGxpbl9tb2RlbCkKbGluX01BRCA8LSBNQUQobGluX21vZGVsKQpsaW5fTVNFIDwtIG1lYW4oc3VtbWFyeShsaW5fbW9kZWwpJHJlc2lkdWFsc14gMikKcGFzdGUoIkxpbmVhciBSZWdyZXNzaW9uIG1vZGVsIGhhcyBSLXNxdWFyZWQgb2YiLCBzdW1yX2xpbiRyLnNxdWFyZWQsICJ3aGljaCBleHBsYWlucyB0aGUgbW9kZWwgZml0cyB0aGUgdHJhaW5pbmcgZGF0YSB3ZWxsOyBoYXMgTUFEIG9mIiwgbGluX01BRCwgIndoaWNoIGlzIGFjdHVhbGx5IGhpZ2hlciB0aGFuIFItc3F1YXJlZDsgaGFzIE1TRSBvZiIsIGxpbl9NU0UpCiNMb2dpc3RpYyBtb2RlbApsb2dfTUFEIDwtIE1BRChsb2dfbW9kZWwpIApwYXN0ZSgiTG9naXN0aWMgUmVncmVzc2lvbiBtb2RlbCBoYXMgTUFEIG9mIiwgbG9nX01BRCwgImV4cGxhaW5zIHRoZSBtb2RlbCBmaXRzIHRoZSB0cmFpbmluZyBkYXRhIHdlbGwiKQpgYGAKCmBgYHtyfQojQSBmdW5jdGlvbiB0byBkZXRlcm1pbmUgaWYgdGhlIG1vZGVsIGhhcyBiaWFzIChmYWxzZSBwb3NpdGl2ZSksIGUuZyBwcmVkaWN0aW5nIGEgcHJvamVjdCBmYWlsZWQgd2hlbiB0aGV5IGFjdHVhbGx5IHN1Y2NlZWRlZApiaWFzIDwtIGZ1bmN0aW9uKG1vZGVsKSB7CiAgcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsLCB0eXBlID0gInJlc3BvbnNlIiwgbmV3ZGF0YSA9IHRlc3RfU2V0KQpwcmVkaWN0aW9uIDwtIGlmZWxzZShwcmVkaWN0aW9uID4gMC41LDEsMCkgI3NldCBzdXJ2aXZhbCA9IDEKY29tYmluZSA8LSBkYXRhLmZyYW1lKHByZWRpY3Rpb24sIHRlc3RfU2V0JGVuY29kZV9zdGF0ZSkKI2ZpbHRlciBvdXQgYWxsIGVycm9ycyB0aGUgbW9kZWwgbWFkZQplcnJvciA8LSBzdWJzZXQoY29tYmluZSwgY29tYmluZSRwcmVkaWN0aW9uICE9IGNvbWJpbmUkdGVzdF9TZXQuZW5jb2RlX3N0YXRlKQojZmFsc2UgcG9zaXRpdmUgYXJlIHRoZSBlcnJvcnMgd2l0aCBwZW9wbGUgd2hvIGFyZSBhY3R1YWwgc3Vydml2ZWQKZmFsc2VQb3NpdGl2ZSA8LSBzdWJzZXQoZXJyb3IsIGVycm9yJHRlc3RfU2V0LmVuY29kZV9zdGF0ZSA9PSAxKQpmYWxzZU5lZ2F0aXZlIDwtIHN1YnNldChlcnJvciwgZXJyb3IkdGVzdF9TZXQuZW5jb2RlX3N0YXRlID09IDApCnJldHVybihjKG5yb3coZmFsc2VQb3NpdGl2ZSkvbGVuZ3RoKHByZWRpY3Rpb24pKjEwMCwgbnJvdyhmYWxzZU5lZ2F0aXZlKS9sZW5ndGgocHJlZGljdGlvbikqMTAwKSkKfQoKYGBgCgpgYGB7cn0KI0NPTVBBUkUgQklBUwpmYWxzZVBvcyA8LSBiaWFzKGxvZ19tb2RlbClbMV0gIzIlCmZhbHNlTmVnIDwtIGJpYXMobG9nX21vZGVsKVsyXSAjMTAlCmZhbHNlUG9zX2xpbiA8LSBiaWFzKGxpbl9tb2RlbClbMV0gIzIlCmZhbHNlTmVnX2xpbiA8LSBiaWFzKGxpbl9tb2RlbClbMl0gIzEwJQpwYXN0ZSgiTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbCBhbmQgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwgaGFzIG1vcmUgZmFsc2UgcG9zaXRpdmUuIEhlbmNlIHRoZXkgYXJlIGJpYXMgdG93YXJkcyBwcmVkaWN0aW5nIGEgcHJvamVjdCBmYWlsIikKCmBgYAoKYGBge3J9CiNmdW5jdGlvbiBnZXRfYWNjdXJhY3k6IHRha2VzIGluIGEgbW9kZWwgYW5kIG91dHB1dCB0aGUgcGVyY2VudGFnZSBvZiBob3cgY29ycmVjdCB0aGUgcHJlZGljdGlvbiAKZ2V0X2FjY3VyYWN5IDwtIGZ1bmN0aW9uKG5hbWUsIG1vZGVsKSB7CiAgcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsLCB0eXBlID0gInJlc3BvbnNlIiwgbmV3ZGF0YSA9IHRlc3RfU2V0KQogIHByZWRpY3Rpb24gPC0gaWZlbHNlKHByZWRpY3Rpb24gPiAwLjUsMSwwKSAjc2V0IHN1cnZpdmFsID0gMQojYW5kIGRldGVybWluZSBpdHMgcHJlZGljdGlvbiBhY2N1cmFjeSAoYXMgYSBwZXJjZW50YWdlIGNvcnJlY3QpLgojYnkgY29tcGFyaW5nIHRoZSBwcmVkaWN0aW9uIHRvIHRoZSB0ZXN0IGRhdGEgc2V0IHN1cnZpdmFsIHJlc3VsdApjb3JyZWN0UHJlZGljdGlvbiA8LSBtZWFuKHByZWRpY3Rpb24gPT0gdGVzdF9TZXQkZW5jb2RlX3N0YXRlKQpyZXR1cm4ocGFzdGUoJ1ByZWRpY3Rpb24gQWNjdXJhY3kgb2YnLCBuYW1lLCAnaXMnLCBjb3JyZWN0UHJlZGljdGlvbiAqIDEwMCwgJyUnKSkKfQpgYGAKCmBgYHtyfQojQ09NUEFSRSBUSEUgQUNDVVJBQ1kgT0YgTU9ERUxTCnBhc3RlKGdldF9hY2N1cmFjeSgiTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwiLCBsaW5fbW9kZWwpLCAid2l0aCBSLXNxdWFyZWQgaXMgMC41MSIpCmdldF9hY2N1cmFjeSgiTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbCIsIGxvZ19tb2RlbCkKcGFzdGUoIlByZWRpY3Rpb24gQWNjdXJhY3kgb2Ygay1OTiBNb2RlbCB3aXRoIGs9MTEgaXMiLCBrbm5fYWNjdXJhY3ksICIlIikKcGFzdGUoIlRoZSByZWdyZXNzaW9uIG1vZGVscyBoYXZlIHZlcnkgc2ltaWxhciBjbG9zZSByZXN1bHRzLiBUaG91Z2ggbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgaXMgaGFzIE1BRCBoaWdoZXIgdGhhbiBSLXNxdWFyZWQsIGJvdGggZml0IHRoZSB0cmFpbmluZyBkYXRhIHdlbGwuIENvbXBhcmVkIHRvIGstTk4sIHRoZXkgYXJlIGJlc3QgcHJlZGljdG9ycyBvZiBhIHN1Y2Nlc2Z1bCBwcm9qZWN0IHRob3VnaCBoYXMgMTAlIGZhbHNlIHBvc2l0aXZlIGJpYXMuIikKCmBgYAoKYGBge3J9CiNQUkVESUNUIFRIRSBMSVZFIFBST0pFQ1RTIFVTSU5HIE1VTFRJUExFIExJTkVBUiBSRUdSRVNTSU9OOiAKI3ByZWRpY3QgdGhlIGFjdGl2ZSBwcm9qZWN0cyB0aGF0IHdlcmUgbGl2ZSB1bnRpbCBsYXN0IG1vbnRoCiNmb3JtYXQgdGhlIGRhdGEgZnJhbWUKYWN0aXZlX2RmIDwtIHN1YnNldChyYXdfZGYsIHJhd19kZiRJRCAlaW4lIGFjdGl2ZV9kZiRJRCkKYWN0aXZlX2RmJGR1cmF0aW9uIDwtIGFzLm51bWVyaWMoYXMuRGF0ZShhY3RpdmVfZGYkZGVhZGxpbmUpIC0gYXMuRGF0ZShhY3RpdmVfZGYkbGF1bmNoZWQpKQphY3RpdmVfZGYkZW5jb2RlX2NhdGVnb3J5IDwtIGFzLm51bWVyaWMoYWN0aXZlX2RmJG1haW5fY2F0ZWdvcnkpCmFjdGl2ZV9kZiRlbmNvZGVfY291bnRyeSA8LSBhcy5udW1lcmljKGFjdGl2ZV9kZiRjb3VudHJ5KQoKI3ByZWRpY3Qgc3VjY2Vzc2Z1bG5lc3MgdXNpbmcgbGluZWFyIHJlZ3Jlc3Npb24KcHJlZF92YWx1ZXMgPC0gYygpCmZvcihpIGluIDE6bnJvdyhhY3RpdmVfZGYpKSB7CiAgcHJlZF92YWx1ZXMgPC0gYyhwcmVkX3ZhbHVlcywgcHJlZGljdF92YWx1ZShhY3RpdmVfZGYsIGxpbl9tb2RlbCwgaSkpCn0KcHJlZF92YWx1ZXMgPC0gaWZlbHNlKHByZWRfdmFsdWVzID4gMC41LDEsMCkgCmFjdGl2ZV9kZiRwcmVkaWN0X3N0YXRlIDwtIHByZWRfdmFsdWVzCmFjdGl2ZV9kZgpgYGAKCmBgYHtyfQojZ2V0IHRoZSBhdmVyYWdlIGRhdGEKbiA8LSBsZW5ndGgoYWN0aXZlX2RmKQpnb29kX3Byb2ogPC0gc3Vic2V0KGFjdGl2ZV9kZiwgYWN0aXZlX2RmJHByZWRpY3Rfc3RhdGUgPT0gMSkKcGVyY2VudGFnZV9zdWNjZXNmdWwgPC0gbnJvdyhnb29kX3Byb2opIC8gbiAqIDEwMApjb3VudHJ5IDwtIG5hbWVzKHdoaWNoLm1heCh0YWJsZShnb29kX3Byb2okY291bnRyeSkpKQphdmdfYmFja2VycyA8LSBtZWFuKGdvb2RfcHJvaiRiYWNrZXJzKQphdmdfZ29hbCA8LSBtZWFuKGdvb2RfcHJvaiR1c2RfZ29hbF9yZWFsKQphdmdfcGxlZGdlZCA8LSBtZWFuKGdvb2RfcHJvaiR1c2RfcGxlZGdlZF9yZWFsKQphdmdfZHVyYXRpb24gPC0gbWVhbihnb29kX3Byb2okZHVyYXRpb24pCiNJTlRFUlBSRVRBVElPTiBPRiBSRVNVTFRTOiAKcGFzdGUoIk9uIHRoZSBwcmVkaWN0aW9uIHJlc3VsdHMgb2YgYWN0aXZlIHByb2plY3RzLCIsIHBlcmNlbnRhZ2Vfc3VjY2VzZnVsLCAiJSBhcmUgc3VjY2Vzc2Z1bC4iKQpwYXN0ZSgiRnJvbSBteSBhdmVyYWdlIGNhbGN1bGF0aW9uLCBpbiBvcmRlciB0byBiZSBzdWNjZXNzZnVsIHByb2plY3Qgb24gS2lja3N0YXJ0ZXIsIHRoZSBjcmVhdG9yIHNob3VsZCBhaW0gdG8gbGF1bmNoIGluICIsIGNvdW50cnksICJ3aXRoIGEgZ29vZCBhbW91bnQgb2YgYmFja2VycyBvZiBhdCBsZWFzdCIsIGNlaWxpbmcoYXZnX2JhY2tlcnMpLCAic2V0dGluZyBhIGdvYWwgbG93ZXIgdGhhbiAkIiwgYXZnX2dvYWwsICJhbmQgYSBkdXJhdGlvbiBvZiBmdW5kLXJhaXNpbmcgYXMgc2hvcnQgYXMiLCBmbG9vcihhdmdfZHVyYXRpb24pLCAiZGF5cyIpCmBgYAoKCgoKCgo=